/*
 * Copyright 2008 Zoundry LLC, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.zoundry.gaedt.ui.views;

import java.util.Set;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.part.ViewPart;
import org.zoundry.gaedt.services.applications.IZGaeApplication;
import org.zoundry.gaedt.services.applications.IZGaeApplicationsServiceObserver;
import org.zoundry.gaedt.services.applications.ZGaeApplicationsService;
import org.zoundry.gaedt.ui.views.actions.ZGaeBrowseApplicationAction;
import org.zoundry.gaedt.ui.views.actions.ZGaeDebugApplicationAction;
import org.zoundry.gaedt.ui.views.actions.ZGaeOpenApplicationAction;
import org.zoundry.gaedt.ui.views.actions.ZGaeStartApplicationAction;
import org.zoundry.gaedt.ui.views.actions.ZGaeStopApplicationAction;
import org.zoundry.gaedt.ui.views.actions.ZGaeUpdateApplicationAction;


/**
 * The GAE Applications View.
 *
 * FIXME this view should grab focus/activation when the status of an application changes (stopped -> running, etc)
 */
public class ZGaeApplicationsView extends ViewPart implements IZGaeApplicationsServiceObserver
{
    /** The table of applications. */
    private TableViewer mViewer;
    /** The application service. */
    private ZGaeApplicationsService mApplicationService;

    /**
     * The constructor.
     */
    public ZGaeApplicationsView()
    {
        setApplicationService(ZGaeApplicationsService.getInstance());
    }

    /**
     * This is a call-back that will allow us to create the viewer and
     * initialize it.
     *
     * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
     */
    @Override
    public void createPartControl(Composite parent)
    {
        mViewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL);

        Table table = mViewer.getTable();
        table.setHeaderVisible(true);
        createTableColumns(table);
        mViewer.setColumnProperties(new String[] { "Application Name", "Status" });

        mViewer.setContentProvider(new ZGaeApplicationsViewContentProvider());
        mViewer.setLabelProvider(new ZGaeApplicationsViewLabelProvider());
        mViewer.setSorter(new ViewerSorter());
        mViewer.setInput(getApplicationService().getGoogleApplications());
        hookContextMenu();
        hookDoubleClickAction();
        contributeToActionBars();
        getApplicationService().addObserver(this);
    }

    /**
     * Creates the columns for the table of applications.
     *
     * @param aTable
     */
    private void createTableColumns(Table aTable)
    {
        TableColumn column = new TableColumn(aTable, SWT.NULL);
        column.setText("Name");
        column.setWidth(200);

        column = new TableColumn(aTable, SWT.NULL);
        column.setText("Status");
        column.setWidth(100);
    }

    /**
     * Hook the context menu, adding in our custom actions.
     */
    private void hookContextMenu()
    {
        MenuManager menuMgr = new MenuManager("#PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener()
        {
            public void menuAboutToShow(IMenuManager aManager)
            {
                fillContextMenu(aManager);
            }
        });
        Menu menu = menuMgr.createContextMenu(mViewer.getControl());
        mViewer.getControl().setMenu(menu);
        mViewer.getTable().addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent aE)
            {
                onSelectionChanged(getSelectedApplication());
            }
        });
        getSite().registerContextMenu(menuMgr, mViewer);
    }

    /**
     * Add the actions to the context menu.
     *
     * @param aManager
     */
    private void fillContextMenu(IMenuManager aManager)
    {
        aManager.add(getOpenAction());
        aManager.add(getBrowseAction());
        aManager.add(new Separator());
        aManager.add(getStartAction());
        aManager.add(getDebugAction());
        aManager.add(getStopAction());
        aManager.add(new Separator());
        aManager.add(getDeployAction());
        aManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    /**
     * Make contributions to the action bars.
     */
    private void contributeToActionBars()
    {
        IActionBars bars = getViewSite().getActionBars();
        fillLocalPullDown(bars.getMenuManager());
        fillLocalToolBar(bars.getToolBarManager());
        bars.updateActionBars();
    }

    /**
     * Make contributions to the pull-down menu.
     *
     * @param aManager
     */
    private void fillLocalPullDown(IMenuManager aManager)
    {
//        aManager.removeAll();
//        aManager.add(getDebugAction());
    }

    /**
     * Make contributions to the tool bar.
     *
     * @param aManager
     */
    private void fillLocalToolBar(IToolBarManager aManager)
    {
        aManager.removeAll();
        aManager.add(getStartAction());
        aManager.add(getDebugAction());
        aManager.add(getStopAction());
    }

    /**
     * Hook into the table's double-click event.
     */
    private void hookDoubleClickAction()
    {
        mViewer.addDoubleClickListener(new IDoubleClickListener()
        {
            /**
             * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
             */
            public void doubleClick(DoubleClickEvent aEvent)
            {
                Event event = new Event();
                event.data = aEvent.getSelection();
                getOpenAction().runWithEvent(event);
            }
        });
    }

    /**
     * Returns the {@link IZGaeApplication} that is currently selected in the table.
     */
    protected IZGaeApplication getSelectedApplication()
    {
        IStructuredSelection selection = (IStructuredSelection) mViewer.getSelection();
        if (!selection.isEmpty())
            return (IZGaeApplication) selection.getFirstElement();
        return null;
    }

    /**
     * Passing the focus request to the viewer's control.
     */
    @Override
    public void setFocus()
    {
        mViewer.getControl().setFocus();
    }

    /**
     * Called when the selection changes.
     *
     * @param aSelectedApplication
     */
    protected void onSelectionChanged(IZGaeApplication aSelectedApplication)
    {
        refreshUI();
    }

    /**
     * Refreshes the UI elements (buttons, menus, etc).
     */
    private void refreshUI()
    {
        contributeToActionBars();
    }

    /**
     * @see org.zoundry.gaedt.services.applications.IZGaeApplicationsServiceObserver#onUpdate(org.zoundry.gaedt.services.applications.ZGaeApplicationsService)
     */
    public void onUpdate(ZGaeApplicationsService aService)
    {
        Display.getDefault().asyncExec(new Runnable()
        {
            /**
             * @see java.lang.Runnable#run()
             */
            public void run()
            {
                mViewer.setInput(getApplicationService().getGoogleApplications());
                refreshUI();
            }
        });
    }

    /**
     * Creates and returns an action.
     */
    private IAction getDeployAction()
    {
        return new ZGaeUpdateApplicationAction(mViewer, getApplicationService());
    }

    /**
     * Creates and returns an action.
     */
    private IAction getStopAction()
    {
        return new ZGaeStopApplicationAction(mViewer, getApplicationService());
    }

    /**
     * Creates and returns an action.
     */
    private IAction getDebugAction()
    {
        return new ZGaeDebugApplicationAction(mViewer, getApplicationService());
    }

    /**
     * Creates and returns an action.
     */
    private IAction getStartAction()
    {
        return new ZGaeStartApplicationAction(mViewer, getApplicationService());
    }

    /**
     * Creates and returns an action.
     */
    private IAction getOpenAction()
    {
        return new ZGaeOpenApplicationAction(mViewer, getApplicationService());
    }
    /**
     * Creates and returns an action.
     */
    private IAction getBrowseAction()
    {
        return new ZGaeBrowseApplicationAction(mViewer, getApplicationService());
    }

    /**
     * @return the applicationService
     */
    protected ZGaeApplicationsService getApplicationService()
    {
        return mApplicationService;
    }

    /**
     * @param aApplicationService the applicationService to set
     */
    protected void setApplicationService(ZGaeApplicationsService aApplicationService)
    {
        mApplicationService = aApplicationService;
    }

    /**
     * Table content provider.
     */
    private static class ZGaeApplicationsViewContentProvider implements IStructuredContentProvider
    {
        /**
         * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
         */
        public void inputChanged(Viewer v, Object oldInput, Object newInput)
        {
        }

        /**
         * @see org.eclipse.jface.viewers.IContentProvider#dispose()
         */
        public void dispose()
        {
        }

        /**
         * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
         */
        public Object[] getElements(Object aParent)
        {
            Set<IZGaeApplication> googleApplications = (Set<IZGaeApplication>) aParent;
            return googleApplications.toArray();
        }
    }
}
